﻿using System;
using System.Collections.Generic;
using System.Data;
using l1cs.server.templates;
using MySql.Data.MySqlClient;
using l1cs.server.model;
using l1cs.server.model.Instance;
using l1cs.server.server.datatables;
using l1cs.server.utils;
using l1cs.server.serverpackets;

namespace l1cs.server.datatables
{
    class DropTable
    {
        private static Logger _log = Logger.getLogger( typeof( DropTable ).FullName );

        private static DropTable _instance;

        /// <summary>
        /// モンスター毎のドロップリスト
        /// </summary>
        private readonly Dictionary<int, List<L1Drop>> _droplists;

        public static DropTable getInstance()
        {
            if ( _instance == null )
            {
                _instance = new DropTable();
            }
            return _instance;
        }

        private DropTable()
        {/*TTOK*/
            _droplists = new Dictionary<int, List<L1Drop>>();

            String sqlstr = "select * from droplist";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                foreach ( DataRow item in wrapper.GetDataTable( sqlstr ).Rows )
                {
                    int mobId = SqlUtils.toInt( item["mobId"] );
                    int itemId = SqlUtils.toInt( item["itemId"] );
                    int min = SqlUtils.toInt( item["min"] );
                    int max = SqlUtils.toInt( item["max"] );
                    int chance = SqlUtils.toInt( item["chance"] );

                    L1Drop drop = new L1Drop( mobId, itemId, min, max, chance );

                    List<L1Drop> dropList;
                    _droplists.TryGetValue( drop.getMobid(), out dropList );
                    if ( dropList == null )
                    {
                        dropList = new List<L1Drop>();
                        _droplists[drop.getMobid()] = dropList;
                    }
                    dropList.Add( drop );
                }
            }
        }

        /// <summary>
        /// モンスターのインベントリにドロップを設定
        /// </summary>
        /// <param name="npc"></param>
        /// <param name="inventory"></param>
        public void setDrop( L1NpcInstance npc, L1Inventory inventory )
        {/*TTOK*/
            // ドロップリストの取得
            int mobId = npc.getNpcTemplate().get_npcId();
            List<L1Drop> dropList;
            _droplists.TryGetValue( mobId, out dropList );
            if ( dropList == null )
            {
                return;
            }

            // レート取得
            double droprate = Config.RATE_DROP_ITEMS;
            if ( droprate <= 0 )
            {
                droprate = 0;
            }
            double adenarate = Config.RATE_DROP_ADENA;
            if ( adenarate <= 0 )
            {
                adenarate = 0;
            }
            if ( droprate <= 0 && adenarate <= 0 )
            {
                return;
            }

            int itemId;
            int itemCount;
            int addCount;
            int randomChance;
            L1ItemInstance item;
            Random random = new Random();

            foreach ( L1Drop drop in dropList )
            {
                // ドロップアイテムの取得
                itemId = drop.getItemid();
                if ( adenarate == 0 && itemId == L1ItemId.ADENA )
                {
                    continue; // アデナレート０でドロップがアデナの場合はスルー
                }

                // ドロップチャンス判定
                randomChance = random.Next( 0xf4240 ) + 1;
                double rateOfMapId = MapsTable.getInstance().getDropRate( npc.getMapId() );
                double rateOfItem = DropItemTable.getInstance().getDropRate( itemId );
                if ( droprate == 0 ||
                     drop.getChance() * droprate * rateOfMapId * rateOfItem < randomChance )
                {
                    continue;
                }

                // ドロップ個数を設定
                double amount = DropItemTable.getInstance().getDropAmount( itemId );
                int min = (int)( drop.getMin() * amount );
                int max = (int)( drop.getMax() * amount );

                itemCount = min;
                addCount = max - min + 1;
                if ( addCount > 1 )
                {
                    itemCount += random.Next( addCount );
                }
                if ( itemId == L1ItemId.ADENA )
                { // ドロップがアデナの場合はアデナレートを掛ける
                    itemCount = (int)( itemCount * adenarate );
                }
                if ( itemCount < 0 )
                {
                    itemCount = 0;
                }
                if ( itemCount > 2000000000 )
                {
                    itemCount = 2000000000;
                }

                // アイテムの生成
                item = ItemTable.getInstance().createItem( itemId );
                item.setCount( itemCount );

                // アイテム格納
                inventory.storeItem( item );
            }
        }

        // ドロップを分配
        public void dropShare( L1NpcInstance npc, List<L1Character> acquisitorList, List<int> hateList )
        {
            L1Inventory inventory = npc.getInventory();
            if ( inventory.getSize() == 0 )
            {
                return;
            }
            if ( acquisitorList.Count != hateList.Count )
            {
                return;
            }
            // ヘイトの合計を取得
            int totalHate = 0;
            L1Character acquisitor;
            for ( int i = hateList.Count - 1; i >= 0; i-- )
            {
                acquisitor = acquisitorList[i];
                if ( ( Config.AUTO_LOOT == 2 ) // オートルーティング２の場合はサモン及びペットは省く
                        && ( acquisitor is L1SummonInstance || acquisitor is L1PetInstance ) )
                {
                    acquisitorList.RemoveAt( i );
                    hateList.RemoveAt( i );
                }
                else if ( acquisitor != null
                      && acquisitor.getMapId() == npc.getMapId()
                      && acquisitor.getLocation().getTileLineDistance(
                              npc.getLocation() ) <= Config.LOOTING_RANGE )
                {
                    totalHate += hateList[i];
                }
                else
                { // nullだったり死んでたり遠かったら排除
                    acquisitorList.RemoveAt( i );
                    hateList.RemoveAt( i );
                }
            }

            // ドロップの分配
            L1ItemInstance item;
            L1Inventory targetInventory = null;
            L1PcInstance player;
            L1PcInstance[] partyMember;
            Random random = new Random();
            int randomInt;
            int chanceHate;
            int itemId;
            for ( int i = inventory.getSize(); i > 0; i-- )
            {
                item = (L1ItemInstance)inventory.getItems()[0];
                itemId = item.getItemId();
                bool isGround = false;
                if ( item.getItem().getType2() == 0 && item.getItem().getType() == 2 )
                { // light系アイテム
                    item.setNowLighting( false );
                }

                if ( ( ( Config.AUTO_LOOT != 0 ) || itemId == L1ItemId.ADENA )
                        && totalHate > 0 )
                { // オートルーティングかアデナで取得者がいる場合
                    randomInt = random.Next( totalHate );
                    chanceHate = 0;
                    for ( int j = hateList.Count - 1; j >= 0; j-- )
                    {
                        chanceHate += hateList[j];
                        if ( chanceHate > randomInt )
                        {
                            acquisitor = (L1Character)acquisitorList[j];
                            if ( itemId >= 40131 && itemId <= 40135 )
                            {
                                if ( !( acquisitor is L1PcInstance ) || hateList.Count > 1 )
                                {
                                    targetInventory = null;
                                    break;
                                }
                                player = (L1PcInstance)acquisitor;
                                if ( player.getQuest().get_step( L1Quest
                                        .QUEST_LYRA ) != 1 )
                                {
                                    targetInventory = null;
                                    break;
                                }
                            }
                            if ( acquisitor.getInventory().checkAddItem( item,
                                    item.getCount() ) == L1Inventory.OK )
                            {
                                targetInventory = acquisitor.getInventory();
                                if ( acquisitor is L1PcInstance )
                                {
                                    player = (L1PcInstance)acquisitor;
                                    L1ItemInstance l1iteminstance = player
                                            .getInventory().findItemId( L1ItemId.ADENA ); // 所持アデナをチェック
                                    if ( l1iteminstance != null
                                            && l1iteminstance.getCount() > 2000000000 )
                                    {
                                        targetInventory = L1World.getInstance()
                                                .getInventory( acquisitor.getX(),
                                                        acquisitor.getY(),
                                                        acquisitor.getMapId() ); // 持てないので足元に落とす
                                        isGround = true;
                                        player.sendPackets( new S_ServerMessage( 166,
                                                "所持しているアデナ",
                                                "2,000,000,000を超過しています。" ) ); // \f1%0が%4%1%3%2
                                    }
                                    else
                                    {
                                        if ( player.isInParty() )
                                        { // パーティの場合
                                            partyMember = player.getParty()
                                                    .getMembers();
                                            for ( int p = 0; p < partyMember.Length; p++ )
                                            {
                                                partyMember[p]
                                                        .sendPackets( new S_ServerMessage(
                                                                813, npc.getName(),
                                                                item.getLogName(),
                                                                player.getName() ) );
                                            }
                                        }
                                        else
                                        {
                                            // ソロの場合
                                            player.sendPackets( new S_ServerMessage(
                                                    143, npc.getName(), item
                                                            .getLogName() ) ); // \f1%0が%1をくれました。
                                        }
                                    }
                                }
                            }
                            else
                            {
                                targetInventory = L1World.getInstance()
                                        .getInventory( acquisitor.getX(),
                                                acquisitor.getY(),
                                                acquisitor.getMapId() ); // 持てないので足元に落とす
                                isGround = true;
                            }
                            break;
                        }
                    }
                }
                else
                { // ノンオートルーティング
                    List<int> dirList = new List<int>();
                    for ( int j = 0; j < 8; j++ )
                    {
                        dirList.Add( j );
                    }
                    int x = 0;
                    int y = 0;
                    int dir = 0;
                    do
                    {
                        if ( dirList.Count == 0 )
                        {
                            x = 0;
                            y = 0;
                            break;
                        }
                        randomInt = random.Next( dirList.Count );
                        dir = dirList[randomInt];
                        dirList.RemoveAt( randomInt );
                        switch ( dir )
                        {
                            case 0:
                                x = 0;
                                y = -1;
                                break;
                            case 1:
                                x = 1;
                                y = -1;
                                break;
                            case 2:
                                x = 1;
                                y = 0;
                                break;
                            case 3:
                                x = 1;
                                y = 1;
                                break;
                            case 4:
                                x = 0;
                                y = 1;
                                break;
                            case 5:
                                x = -1;
                                y = 1;
                                break;
                            case 6:
                                x = -1;
                                y = 0;
                                break;
                            case 7:
                                x = -1;
                                y = -1;
                                break;
                        }
                    } while ( !npc.getMap().isPassable( npc.getX(), npc.getY(), dir ) );
                    targetInventory = L1World.getInstance().getInventory(
                            npc.getX() + x, npc.getY() + y, npc.getMapId() );
                    isGround = true;
                }
                if ( itemId >= 40131 && itemId <= 40135 )
                {
                    if ( isGround || targetInventory == null )
                    {
                        inventory.removeItem( item, item.getCount() );
                        continue;
                    }
                }
                inventory.tradeItem( item, item.getCount(), targetInventory );
            }
            npc.turnOnOffLight();
        }
    }
}
